package com.tree.lambda;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @ClassName com.tree.lambda
 * Description: <类功能描述>. <br>
 * <p>
 * <使用说明>
 * </p>
 * @Author tree
 * @Date 2019-08-24 21:49
 * @Version 1.0
 */
public class LambadaExample {
    private static List<User> userList = getUserList();


    public static void main(String[] args) {
//        groupBy();


    }

    /**
     * 一 获取Stream
     */
    public static void getStream(){
        // 1、数组
        String[] arr = new String[]{"ab", "cd", "ef"};
        Stream<String> stringStream = Arrays.stream(arr);


        // 2、集合
        List<String> list = Arrays.asList("ab", "cd", "ef");
        Stream<String> listStream = list.stream();

        // 3、值
        Stream<String> stream = Stream.of("ab", "cd", "ef");

    }

    /**
     * for-each
     */
    public static void forEach(){
        for(User user: userList){
            System.out.println(user);
        }

        System.out.println("===============");
        // java 8 lambda
        userList.forEach(user -> {
            System.out.println(user);
        });

        System.out.println("===============");
        //java 8 stream
        userList.stream().forEach(user -> {
            System.out.println(user);
        });

    }

    /**
     * sort
     * TODO 注意双冒号用法????
     */
    public static void sort(){
        System.out.println("-----排序前-----");
        userList.forEach(user -> System.out.println(user));
        System.out.println("-----排序后-----");
        // java 8 前
        System.out.println("java 8 前");
        Collections.sort(userList, new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return o1.getAge().compareTo(o2.getAge());
            }
        });

        for (User user : userList) {
            System.out.println(user);
        }

        // java 8 stream 方法引用
        System.out.println("java 8 stream 方法引用,反序");
        userList.stream().sorted(Comparator.comparing(User::getAge).reversed()).forEach(user -> {
            System.out.println(user);
        });

    }



    /**
     * filter
     */
    public static void filter(){
        // 输出年龄大于50的人
        System.out.println("-----过滤前-----");
        userList.forEach(user -> System.out.println(user));
        System.out.println("-----过滤后-----");
        // java 8 前
        System.out.println("java 8 前");
        for(User user: userList){
            if (user.getAge() > 50) {
                System.out.println(user);
            }
        }
        // java 8 stream
        System.out.println("java 8 stream");
        userList.stream().filter(user -> user.getAge()>50).forEach(user->System.out.println(user));

        System.out.println("java 8 stream 过滤后");
        for(User user: userList){
            if (user.getAge() > 50) {
                System.out.println(user);
            }
        }
    }

    /**
     * limit
     */
    public static void limit (){
        // 从第三个开始截断，只输出前三个
        System.out.println("-----截断前-----");
        userList.forEach(user -> System.out.println(user));
        System.out.println("-----截断后-----");
        // java 8 前
        System.out.println("java 8 前");
        for (int i = 0; i < 1; i++) {
            System.out.println(userList.get(i));
        }
        // java 8 stream
        System.out.println("java 8 stream");
        userList.stream().limit(3).forEach(user -> System.out.println(user));
        System.out.println(111);
        userList.stream().limit(4).forEach(user -> System.out.println(user));

    }

    /**
     * skip()：与limit互斥，使用该方法跳过元素
     */
    public static void skip(){
        // 跳过前三个元素，从第四个开始输出
        System.out.println("-----跳过前-----");
        userList.forEach(user -> System.out.println(user));
        System.out.println("-----跳过后-----");
        // java 8 前
        System.out.println("java 8 前");
        for (int i = 3; i < userList.size(); i++) {
            System.out.println(userList.get(i));
        }
        // java 8 stream
        System.out.println("java 8 stream");
        userList.stream().skip(5).forEach(user -> System.out.println(user));
    }

    /**
     * distinct()：使用该方法去重，注意：必须重写对应泛型的hashCode()和equals()方法
     */
    public static void distinct(){
        // 因为Arrays.asList() 返回的是Arrays的内部类ArrayList，操作remove，add会报错
        List<User> users = new ArrayList<>(userList);
        // 为list去除重复数据
        System.out.println("-----去重前-----");
        userList.forEach(user -> System.out.println(user));
        System.out.println("-----去重后-----");
        // java 8 前
        System.out.println("java 8 前");
        for (int i = 0; i < users.size() - 1; i++) {
            for (int j = users.size() - 1; j > i; j--) {
                if (users.get(j).getAge().equals(users.get(i).getAge())
                        && users.get(j).getUserName().equals(users.get(i).getUserName())
                    ) {
                    users.remove(i);
                }
            }
        }
        for (User user : users) {
            System.out.println(user);
        }
        // java 8 stream
        System.out.println("java 8 stream");
        userList.stream().distinct().forEach(user -> System.out.println(user));

        //根据上述方法，完成去重+按照年龄大于40以后从小到大+只取前二
        System.out.println("去重+按照年龄大于40以后从小到大+只取前二");
        userList.stream().distinct().filter(user->user.getAge()>40).sorted(Comparator.comparing(User::getAge)).limit(2).forEach(user -> System.out.println(user));
    }

    /**
     * max，min，sum，avg，count
     * 使用2种方法
     * 1. 利用SummaryStatistics
     * 2. 利用Collectors
     */
    public static void aggregation(){
        Stream<User> stream = userList.stream();
        IntSummaryStatistics num = stream.mapToInt(u -> u.getAge())
                .summaryStatistics();
        System.out.println("总共人数：" + num.getCount());
        System.out.println("总共人数：" + userList.stream().collect(Collectors.counting()));
        System.out.println("平均年龄：" + num.getAverage());
        System.out.println("平均年龄：" + userList.stream().collect(Collectors.averagingDouble(User::getAge)));
        System.out.println("最大年龄：" + num.getMax());
        System.out.println("最大年龄：" + userList.stream().max(Comparator.comparing(User::getAge)).get().getAge());
        System.out.println("最小年龄：" + num.getMin());
        System.out.println("最小年龄：" + userList.stream().min(Comparator.comparing(User::getAge)).get().getAge());
        System.out.println("年龄之和：" + num.getSum());
        System.out.println("年龄之和：" + userList.stream().collect(Collectors.summingDouble(User::getAge)));
    }

    /**
     * map()：接收一个方法作为参数，该函数会被应用到每个元素上，并将其映射成一个新的元素
     */
    public static void map(){
        // 只输出所有人的年龄
        userList.stream().forEach(user -> System.out.println(user));
        System.out.println("映射后----->");
        List<Integer> ages = userList.stream().map(User::getAge).collect(Collectors.toList());
        ages.forEach(age -> System.out.println(age));

        // 小写转大写
        List<String> words = Arrays.asList("aaa", "vvvv", "cccc");
        System.out.println("全部大写---->");
        List<String> collect = words.stream().map(s->s.toUpperCase()).collect(Collectors.toList());
        collect.forEach(s-> System.out.println(s));

    }

    /**
     * 9、flatMap()：对每个元素执行mapper指定的操作，并用所有mapper返回的Stream中的元素组成一个新的Stream作为最终返回结果，
     * 通俗易懂就是将原来的stream中的所有元素都展开组成一个新的stream
     */
    public static void flatMap(){
        //创建一个 装有两个泛型为integer的集合
        Stream<List<Integer>> stream = Stream.of(Arrays.asList(1, 2, 3), Arrays.asList(4, 5));
        // 将两个合为一个
        Stream<Integer> integerStream = stream.flatMap(integers -> integers.stream());

        // 为新的集合
        List<Integer> collect = integerStream.collect(Collectors.toList());
        System.out.println("新stream大小:"+collect.size());
        System.out.println("-----合并后-----");
        collect.forEach(o -> System.out.println(o));

    }

    /**
     *findFirst() ：使用该方法获取第一个元素
     */
    public static void testFindFirst(){
        User user = userList.stream().findFirst().get();
        System.out.println(user);
    }

    /**
     * groupby
     * https://github.com/Darlen/JavaLambdaInternals/blob/master/5-Streams%20API(II).md
     */
    public static void groupBy(){
        System.out.println("按人分组统计数量");
        System.out.println(userList.stream().collect(Collectors.groupingBy(User::getUserName,Collectors.counting())));
        System.out.println("按人分组，并打印每个分组的年龄");
        System.out.println(userList.stream().collect(Collectors.groupingBy(User::getUserName,Collectors.mapping(User::getAge,Collectors.toList()))));
    }


    /**
     * peek
     * 主要用于debug输出每步的中间值
     */
    public static void peek(){
        /**
         * 输出为： A1B1C1 A2B2C2 A3B3C3 中间操作是懒惰的，也就是中间操作不会对数据做任何操作，
         * 直到遇到了最终操作。而最终操作，都是比较热情的。他们会往前回溯所有的中间操作。也就是当执行到最后的forEach操作的时候，
         * 它会回溯到它的上一步中间操作，上一步中间操作，又会回溯到上上一步的中间操作，...，直到最初的第一步。
         * 第一次forEach执行的时候，会回溯peek 操作，然后peek会回溯更上一步的limit操作，然后limit会回溯更上一步的peek操作，
         * 顶层没有操作了，开始自上向下开始执行，输出：A1B1C1
         * 第二次forEach执行的时候，然后会回溯peek 操作，然后peek会回溯更上一步的limit操作，然后limit会回溯更上一步的peek操作，
         * 顶层没有操作了，开始自上向下开始执行，输出：A2B2C2
         *
         * ... 当第四次forEach执行的时候，然后会回溯peek 操作，然后peek会回溯更上一步的limit操作，到limit的时候，
         * 发现limit(3)这个job已经完成，这里就相当于循环里面的break操作，跳出来终止循环
         */
        IntStream.range(1, 10)
                .peek(x -> System.out.println("\n第一步" + x))
                .limit(3)
                .peek(x -> System.out.println("第二步" + x))
                .forEach(x -> System.out.println("第三步" + x));


        /**
         * 输出为： A1 A2 A3 A4 A5 A6 A7B7C7 A8B8C8 A9B9C9
         * 第一次forEach执行的时候，会回溯peek操作，
         * 然后peek会回溯更上一步的skip操作，skip回溯到上一步的peek操作，顶层没有操作了，开始自上向下开始执行，执行到skip的时候，
         * 因为执行到skip，这个操作的意思就是跳过，下面的都不要执行了，也就是就相当于循环里面的continue，结束本次循环。输出：A1
         *
         * 第二次forEach执行的时候，会回溯peek操作，然后peek会回溯更上一步的skip操作，skip回溯到上一步的peek操作，顶层没有操作了，
         * 开始自上向下开始执行，执行到skip的时候，发现这是第二次skip，结束本次循环。输出：A2
         *
         * ...
         *
         * 第七次forEach执行的时候，会回溯peek操作，然后peek会回溯更上一步的skip操作，skip回溯到上一步的peek操作，顶层没有操作了，
         * 开始自上向下开始执行，执行到skip的时候，发现这是第七次skip，已经大于6了，它已经执行完了skip(6)的job了。
         * 这次skip就直接跳过，继续执行下面的操作。输出：A7B7C7
         *
         * ...直到循环结束。
         */
        IntStream.range(1, 10)
                .peek(x -> System.out.print("\nA" + x))
                .skip(6)
                .peek(x -> System.out.print("B" + x))
                .forEach(x -> System.out.print("C" + x));
    }





    /**
     * 构建用户结合
     * @return
     */
    public static List<User> getUserList(){
        List<User> list = Arrays.asList(
                // name，age
                new User(1,"张三", 11),
                new User(2,"王五", 20),
                new User(3,"王五", 91),
                new User(4,"张三", 8),
                new User(5,"李四", 44),
                new User(6,"李四", 44),
                new User(7, "李四", 44)
        );
        return list;
    }


}
